var id;
var play; // play game canvas
var reactants;
var products;
var ridc = 0;  // reactant id counter
var pidc = 0;  // product id counter

var text_color = "#444444";
var NUMBER_OF_ANSWER_CARDS = 5;

var MAX_REACTANTS = 2;
var MAX_PRODUCTS = 3;

var be = new function() {
  var frames;

  var rxns; // reactions
  var cri = 0; // current reaction index
  var crx; // current reaction

  // intro to chemical equations, intro to balancing equations,
  // choose answer card, confirm choice result
  var ibe, rbe, cac, ccr;

  var crx; // create a reaction

  this.init = function(game_id, r) {
    id = game_id;
    rxns = r;

    ibe = $("#ibe")[0];
    rbe = $("#rbe")[0];
    cac = $("#cac")[0];
    ccr = $("#ccr")[0];
    crx = $("#create_reaction")[0];

    if (ibe && rbe) {
      var sample_rxn = new Object();
      sample_rxn.reactants = [ { "formula" : "CH4", "common_name" : "Methane", "quantity" : 1 },
        { "formula" : "O2", "common_name" : "Oxygen", "quantity" : 2 } ];
      sample_rxn.products = [ { "formula" : "CO2", "common_name" : "Carbon Dioxide", "quantity" : 1 },
        { "formula" : "H2O", "common_name" : "Water", "quantity" : 2 } ];
      sample_rxn.image = [{"url" : "/images/logo6.png"}];
      $(ibe).find("canvas")[0].getContext("2d").drawAnswerCard(0, 0, sample_rxn, 100);
      $(rbe).find("canvas.qc")[0].getContext("2d").drawQuestionCard(0, 0, sample_rxn, 80);
      $(rbe).find("canvas.ac")[0].getContext("2d").drawAnswerCard(0, 0, sample_rxn, 80);
    }

    if (cac) {
      cac.qc = $(cac).find("canvas.qc")[0]; // question card
      cac.di = $(cac).find("div.drop-instruction")[0]
      cac.sac = $(cac).find("canvas.sac")[0]; // selected answer card
      cac.confirm = $(cac).find("button.confirm")[0];
      cac.qc.ctx = cac.qc.getContext("2d");
      cac.ac = new Array(); // answer cards
      for (var i = 0; i < NUMBER_OF_ANSWER_CARDS; i++) {
        cac.ac[i] = $(cac).find("canvas#ac_" + i)[0];
        cac.ac[i].ctx = cac.ac[i].getContext("2d");
      }
    }

    if (crx) {
      crx.ac = $(crx).find("canvas.ac")[0];
      crx.ac.getContext("2d").drawAnswerCard(0, 0, null, 100);
      crx.bt = $(crx).find("div.balance")[0];
      crx.crb = $(crx).find("button.create")[0];
    }

    frames = [ibe, rbe, cac, ccr];
  }

  this.showIntro = function() {
    hideDivs(frames);
    $(ibe).show();
  };

  this.showReview = function() {
    hideDivs(frames);
    $(rbe).show();
  };

  this.showCards = function() {
    hideDivs(frames);
    cac.answers = new Array();
    for (var i = 0; i < NUMBER_OF_ANSWER_CARDS; i++) {
      cac.answers[i] = createRandomReaction();
    }
    cac.answers.correct = Math.floor(Math.random() * cac.answers.length);
    cac.answers[cac.answers.correct] = rxns[cri];

    cac.qc.ctx.drawQuestionCard(0, 0, rxns[cri], 72);
    for (var j = 0; j < cac.answers.length; j++) {
      cac.ac[j].ctx.drawAnswerCard(0, 0, cac.answers[j], 72);
    }
    $(cac).show();

    dnd.setStartCallBack(function(s, v) {
      $([cac.sac, cac.confirm]).hide();
      $(cac.di).show();
    });
    dnd.setDropCallBack(dropAnswerCard);
  };

  function dropAnswerCard(target, value) {
    cac.sai = value; // selected answer index
    cac.sac.getContext("2d").drawAnswerCard(0, 0, cac.answers[value], 72);
    $(cac.di).hide();
    $([cac.sac, cac.confirm]).show();
  }

  this.confirmChoice = function() {
    hideDivs(frames);
    $(ccr).find("canvas.qc")[0].getContext("2d").drawQuestionCard(0, 0, rxns[cri], 72);
    $(ccr).find("canvas.ac")[0].getContext("2d").drawAnswerCard(0, 0, cac.answers[cac.sai], 72);
    $(ccr).show();
  }

  this.showReactions = function(creator) {
    var reactions = $("#reactions")[0];
    for (var i = 0; i < rxns.length; i++) {
      var rxn = rxns[i];
      var rs = rxn.reactants;
      var ps = rxn.products;
      var rwcn = ""; // reactants in sentence form with common names
      var rwf = "";  // reactants in equation form with formulas
      var pwcn = ""; // products in sentence form with common names
      var pwf = "";  // products in equation form with formulas
      for (var j = 0; j < rs.length; j++) {
        var r = rs[j];
        rwcn += (r.quantity == 1 ? "" : (r.quantity + " ")) + r.common_name +
          (j + 1 != rs.length ? " and " : "");
        rwf += (r.quantity == 1 ? "" : (r.quantity + " ")) + r.formula +
          (j + 1 != rs.length ? " + " : "");
      }
      for (var k = 0; k < ps.length; k++) {
        var p = ps[k];
        pwcn += (p.quantity == 1 ? "" : (p.quantity + " ")) + p.common_name +
          (k + 1 != ps.length ? " and " : "");
        pwf += (p.quantity == 1 ? "" : (p.quantity + " ")) + p.formula +
          (k + 1 != ps.length ? " + " : "");
      }
      var table = "<table><tr><td style=\"width:180px;\">" +
        "<canvas id=\"qc-" + rxn.id +"\" width=\"180px\" height=\"252px\" style=\"border: 1px solid black;\"></canvas>" +
        "</td><td class=\"reaction-details\">" +
        "<p>Reactants<br />" + rwcn + "</p>" +
        "<p>Products<br />" + pwcn + "</p>" +
//        "<p>Chemical Equation<br />" + rwf + " &rarr; " + pwf + "</p>" +
        "<p>Related Experiment<br />" +
        (creator ? "<a><textarea id=\"re-" + rxn.id + "\" name=\"related_experiment\">" +
          (rxn.related_experiment != null && rxn.related_experiment.length > 0 ?
            rxn.related_experiment : "Write about a related experiment.") +
          "</textarea></a>" : rxn.related_experiment) + "</p>";
      if (creator) {
        if (rxn.image && rxn.image.length > 0) {
          table += "<p><a class=\"f-left\" onclick=\"image.remove('" + rxn.image[0].id + "')\">Remove image</a>";
        } else {
          table += "<p><a class=\"f-left\" onclick=\"image.add(this, '" + rxn.id + "', -48, -192)\">Add image</a>";
        }
        table += "<a class=\"f-right\" onclick=\"game.deletePiece('reaction', '" + rxn.id + "')\">Delete reaction</a></p>";
      }
      table += "</td><td style=\"width:180px;\">" +
        "<canvas id=\"ac-" + rxn.id + "\" width=\"180px\" height=\"252px\" style=\"border: 1px solid black;\"></canvas>" +
        "</td></tr></table>";
      $(reactions).append("<div id=\"rxn-" + rxn.id + "\">" + table + "</div>");
      var reta = $(reactions).find("textarea#re-" + rxn.id);
      reta.blur(function() {
        var id = this.id.substring(3);
        game.savePieceProperty("reaction", rxn.id,  "related_experiment", this.value, function() {
//          alert("saved");
        } );
      } );
      $(reactions).find("canvas#qc-" + rxn.id)[0].getContext("2d").drawQuestionCard(0, 0, rxn, 72);
      $(reactions).find("canvas#ac-" + rxn.id)[0].getContext("2d").drawAnswerCard(0, 0, rxn, 72);
    }
  };

  this.addMolecule = function(type) {
    var molecules = $("#" + type + "s");
    var n = molecules.find(".molecule").length;
    if (type == "reactant" && n >= MAX_REACTANTS || type == "product" && n >= MAX_PRODUCTS) { return; }
    if (n == 0) {
      $(molecules.find("p")).remove();
    }
    molecules.append(moleculeSelect(type));
    if (type == "reactant") { ridc++; }
    else if (type == "product") { pidc++; }
    this.verifyReaction();
    showAddRemoveButtons(type);
  };

  this.removeMolecule = function(type) {
    var molecules = $("#" + type + "s").find(".molecule");
    var n = molecules.length;
    if (n == 0) { return; }
    $(molecules[molecules.length - 1]).remove();
    if (n == 1) {
      $("#" + type + "s").append("<p>Add a " + type + " using the button above</p>");
    }
    if (type == "reactant") { ridc--; }
    else if (type == "product") { pidc--; }
    this.verifyReaction();
    showAddRemoveButtons(type);
  };

  this.verifyReaction = function() {
    var rxn = new Object();
    rxn.reactants = getMolecules("reactants");
    rxn.products = getMolecules("products");
    var re = countElements(rxn.reactants);
    var pe = countElements(rxn.products);
    var valid = ridc > 0 && pidc > 0;
    for (var ri in re) {
      if (re[ri] != pe[ri]) {
        valid = false;
      }
    }
    for (var pi in pe) {
      if (pe[pi] != re[pi]) {
        valid = false;
      }
    }
    $(crx.crb).attr("enabled", valid ? "true" : "false");
    crx.ac.getContext('2d').drawAnswerCard(0, 0, rxn, 100);
    drawBalanceTable(re, pe);
    return valid;
  };

  this.saveReaction = function(k) {
    if ($(crx.crb).attr("enabled") == "false") {
      if ($("tr.element").length > 0) {
        alert("Reaction unbalanced.")
      }
      return;
    }
    var reaction = {
      "reactants" : getMolecules("reactants"),
      "products" : getMolecules("products")
    }
    game.createPiece(id, "reaction", JSON.stringify(reaction),
      function() { window.location.reload(true); } );
  };

  function drawBalanceTable(re, pe) {
    var elements = $("#elements")[0];
    var trs = $(elements).find("tr");
    var e = new Array();
    for (var i = 0; i < trs.length; i++) {
      $(trs[i]).remove();
    }
    for (var ri in re) {
      e.push(ri);
    }
    for (var pi in pe) {
      if ($.inArray(pi, e) < 0) {
        e.push(pi);
      }
    }
    if (e.length > 0) {
      $(elements).append("<tr><td>Element</td><td>Reactants</td><td>Products</td><td></td>");
      for (var ei in e) {
        var element = "<td>" + e[ei] + "</td>";
        var rq = "<td>" + (re[e[ei]] != null ? re[e[ei]] : 0) + "</td>";
        var pq = "<td>" + (pe[e[ei]] != null ? pe[e[ei]] : 0) + "</td>";
        var balanced = rq == pq;
        var css_class = "element " + (balanced ? "balanced" : "unbalanced");
        $(elements).append("<tr class=\"" + css_class + "\">" + element + rq + pq + "<td class=\"balance\">" + (balanced ? "&#10004;" : "&#10008;") + "</td></tr>");
      }
    } else {
      $(elements).append("<tr><td>Add reactants and products to see the balance table</td></tr>");
    }
  }
};

function getMolecules(type) {
  var am = $(".molecule"); // all molecules
  var sm = new Array(); // select molecules
  for (var mi = 0; mi < am.length; mi++) {
    var m = $(am[mi]);
    var pid = m.parent()[0].id;
    if (pid == type) {
      var q = m.find(".quantity")[0].value;
      var f = m.find(".formula")[0].value;
      sm.push(new Molecule(f, null, null, q));
    }
  }
  return sm;
}

function deleteReaction(id) {
  if (!confirm("Are you sure you want to delete this reaction?")) {
    return;
  }
  game.deletePiece("reaction", id);
}

function countElements(ms) {
  var e = new Object();
  for (var mi = 0; mi < ms.length; mi++) {
    var m = ms[mi];
    for (var en in m.atoms) {
      e[en] = (e[en] != null ? e[en] : 0) + m.atoms[en] * m.quantity;
    }
  }
  return e;
}

function showAddRemoveButtons(type) {
  var molecules = $("#" + type + "s");
  var l = molecules.find(".molecule").length;
  var add = $("#add-" + type);
  var remove = $("#remove-" + type);
  if (l == 0) {
    add.removeClass("disabled");
    add.addClass("enabled");
    remove.removeClass("enabled");
    remove.addClass("disabled");
  } else if (0 < l && (type == "reactant" && l < MAX_REACTANTS)
                      || (type == "product" && l < MAX_PRODUCTS )) {
    add.removeClass("disabled");
    add.addClass("enabled");
    remove.removeClass("disabled");
    remove.addClass("enabled");
  } else {
    add.removeClass("enabled");
    add.addClass("disabled");
    remove.removeClass("disabled");
    remove.addClass("enabled");
  }
}

function moleculeSelect(type) {
  var id = "-1";
  if (type == "reactant") {
    id = "r-" + ridc;
  } else if (type == "product") {
    id = "p-" + pidc;
  } else {
    return "";
  }
  var selected = null;
  switch (id) {
    case "r-0" : selected = "NaHCO3"; break;
    case "r-1" : selected = "HCOOH"; break;
    case "p-0" : selected = "H2O"; break;
    case "p-1" : selected = "CO2"; break;
    case "p-2" : selected = "HCOONa"; break;
  }
  var quantity = "<select class= \"quantity\" onchange=\"be.verifyReaction()\">" + quantitiesOptions(selected) + "</select>";
  var formula = "<select class=\"formula\" onchange=\"be.verifyReaction()\">" + moleculesOptions(selected) + "</select>";
  return "<div id=\"" + id + "\" class=\"molecule\">" + quantity + "\n" + formula + "</div>";
}

function quantitiesOptions(selected) {
  var options = "";
  for (var i = 1; i < 5; i++) {
    options = options + "<option>" + i + "</option>";
  }
  return options;
}

function moleculesOptions(selected) {
  var options = "";
  for (var sn in f2m) {
    var ln = f2m[sn].common_name;
    var snf = formatShortName(sn);
    var s = (sn == selected ? " selected=\"selected\"" : "");
    options = options + "<option value=\"" + sn + "\"" + s + ">" + ln + " (" + snf + ")" + "</option>";
  }
  return options;
}

function formatShortName(sn) {
  var f = "";
  for (var i = 0; i < sn.length; i++) {
    var c = sn[i];
    if ('0' <= c && c <= '9') {
      f = f + "&#832" + c + ";";
    } else if (c == '+' || c == '-') {
      f = f + "&#831" + (c == '+' ? "4;" : "5");
    } else {
      f = f + c;
    }
  }
  return f;
}

function createRandomReaction() {
  var formulas = ["H2O", "H2O2", "O2", "CH4", "CO2", "NaHCO3", "HCOOH", "HCOONa"];
  var reactants = [];
  var products = [];
  for (var i = 0; i < 2 + Math.round(Math.random()); i++) {
    reactants[i] = f2m[formulas[Math.floor(Math.random() * formulas.length)]];
  }
  for (var j = 0; j < 2 + Math.round(Math.random()); j++) {
    products[j] = f2m[formulas[Math.floor(Math.random() * formulas.length)]];
  }
  return { "reactants" : reactants, "products" : products };
;
}

CanvasRenderingContext2D.prototype.drawQuestionCard = function(x, y, rxn, scale) {
  var wm = 2.5; // width multiplier
  var hm = 3.5; // height multiplier
  this.fillStyle = "#FFFFFF";
  this.fillRect(x, y, scale * wm, scale * hm);

  var lw = scale / 12;
  this.lineWidth = lw;
  this.strokeStyle = "#3EB846";
  this.drawLine(x + lw / 2, y, x + lw / 2, y + hm * scale);
  this.drawLine(x, y + lw / 2, x + wm * scale, y + lw / 2);
  this.drawLine(x, y + hm * scale - lw / 2, x + wm * scale, y + hm * scale - lw / 2);
  this.drawLine(x + wm * scale - lw / 2, y, x + wm * scale - lw / 2, y + hm * scale);

  if (rxn.image && rxn.image.length > 0) {
    var img = new Image();
    var ctx = this;
    var whr = 1.6;
    img.onload = function() {
      var sw, sh, sx, sy;
      if (img.height * whr > img.width) {
        sw = img.width;
        sh = sw / whr;
        sx = 0;
        sy = (img.height - sh) / whr;
      } else {
        sh = img.height;
        sw = whr * sh;
        sy = 0;
        sx = 0.5 * (img.width - sw);
      }
      var dx = 0.16 * scale;
      var dy = 0.16 * scale;
      var dw = 2.18 * scale;
      var dh = (2.18 / whr) * scale;
      ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);
    };
    img.src = rxn.image[0].url;
  } else {
    this.fillStyle = "#000000";
    this.font = Math.round(0.36 * scale) + "px sans-serif";
    this.drawCenterText("Add Image", 0.5 * wm * scale, 0.24 * hm * scale, wm * scale);
  }

  var r = rxn.reactants;
  this.font = Math.round(0.24 * scale) + "px sans-serif";
  this.fillStyle = "#000000";
  var text0 = "Find the answer card that shows " +r[0].common_name + " (" + r[0].formula +
    ") reacting with " + r[1].common_name + " (" + r[1].formula + ").";
  this.drawCenterText(text0, x + 0.5 * wm * scale, y + 1.8 * scale, 0.84 * wm * scale)
};

CanvasRenderingContext2D.prototype.drawAnswerCard = function(x,y, rxn, scale) {
  var wm = 2.5; // width multiplier
  var hm = 3.5; // height multiplier
  this.fillStyle = "#FFFFFF";
  this.fillRect(x, y, scale * wm, scale * hm);

  var lw = scale / 12;
  this.lineWidth = lw;
  this.strokeStyle = "#FFF33E";
  this.drawLine(x + lw / 2, y, x + lw / 2, y + hm * scale);
  this.drawLine(x, y + lw / 2, x + wm * scale, y + lw / 2);
  this.drawLine(x, y + hm * scale - lw / 2, x + wm * scale, y + hm * scale - lw / 2);
  this.drawLine(x + wm * scale - lw / 2, y, x + wm * scale - lw / 2, y + hm * scale);

  if (rxn == null) {
    return;
  }
  this.drawReaction(x, y, rxn, scale);
};

CanvasRenderingContext2D.prototype.drawReaction = function(x, y, rxn, scale) {
  var wm = 2.5; // width multiplier
  var hm = 3.5; // height multiplier
  this.fillStyle = "#FFFFFF";
  this.fillRect(x + 16, y + 16, wm * scale - 32, hm * scale - 32);

  var lw = scale / 12;
  this.lineWidth = lw;

  this.strokeStyle = "#CCCCCC";
  this.drawLine(x + 0.5 * wm * scale, y + lw, x + 0.5 * wm * scale, y + hm * scale - lw);

  var r = rxn.reactants;
  var p = rxn.products;

  var rm = ["H2O", "H2O2", "O2", "CH4", "CO2", "NaHCO3", "HCOOH", "HCOONa"];
  if (r == null) {
    r = [];
    for (var i = 0; i < 2; i++) {
      var rri = Math.floor(Math.random() * rm.length); // random reactant index
      r.push(f2m[rm[rri]]);
    }
  }
  if (p == null) {
    p = [];
    for (var j = 0; j < 3; j++) {
      var rpi = Math.floor(Math.random() * rm.length); // random product index
      p.push(f2m[rm[rpi]]);
    }
  }

  for (var i = 0; i < r.length; i++) {
    var rx = x + 0.24 * wm * scale;
    var ry = y + 0.45 * hm * scale + (i + 0.5 - r.length * 0.5) * scale;
    this.drawMolecule(r[i].formula, rx, ry, scale);
    if (r[i].quantity > 1) {
      this.font = Math.round(0.2 * scale) + "px sans-serif";
      this.drawCenterText("x " + r[i].quantity, rx + 0.16 * wm * scale, ry + 0.08 * hm * scale);
    }
    if (i + 1 != r.length) {
      this.lineWidth = scale / 32;
      this.strokeStyle = "#444444";
      this.drawLine(rx - 0.08 * scale, ry + 0.5 * scale, rx + 0.08 * scale, ry + 0.5 * scale);
      this.drawLine(rx, ry + 0.5 * scale - 0.08 * scale, rx, ry + 0.5 * scale + 0.08 * scale);
    }
  }
  for (var j = 0; j < p.length; j++) {
    var px = x + 0.75 * wm * scale;
    var py = y + 0.45 * hm * scale + (j + 0.5 - p.length * 0.5) * scale;
    this.drawMolecule(p[j].formula, px, py, scale);
    if (p[j].quantity > 1) {
      this.font = Math.round(0.2 * scale) + "px sans-serif";
      this.drawCenterText("x " + p[j].quantity, px + 0.16 * wm * scale, py + 0.08 * hm * scale);
    }
    if (j + 1 != p.length) {
      this.lineWidth = scale / 32
      this.strokeStyle = "#444444";
      this.drawLine(px - 0.08 * scale, py + 0.5 * scale, px + 0.08 * scale, py + 0.5 * scale);
      this.drawLine(px, py + 0.5 * scale - 0.08 * scale, px, py + 0.5 * scale + 0.08 * scale);
    }
  }

  this.font = Math.round(scale * 0.2) + "px sans-serif";
  this.textAlign = "center";
  this.textBaseline = "bottom";
  this.fillStyle = "#000000";
  this.fillText("Reactants", x + 0.24 * wm * scale, y + (hm - 0.1) * scale, null);
  this.fillText("Products", x + 0.76 * wm * scale, y + (hm - 0.1) * scale, null);

  this.strokeStyle = "#000000";
  this.lineWidth = 0.5 * lw;
  if (r.length > 0 && p.length > 0) {
    this.drawArrow(x + 0.44 * wm * scale, y + 0.45 * hm * scale, x + 0.58 * wm * scale, y + 0.45 * hm * scale);
  }
  this.drawArrow(x + 0.44 * wm * scale, y + 0.945 * hm * scale, x + 0.58 * wm * scale, y + 0.945 * hm * scale);
}

CanvasRenderingContext2D.prototype.drawMolecule = function(sn, x, y, scale) {
  var sm = f2sfmm[sn];
  for (var e in sm) { // e == element shortname
    var aca = sm[e]; // atom coordinates array;
    for (var j in aca) {
      var ac = aca[j];
      this.drawAtom(e, x + 0.20 * ac[0] * scale, y + 0.20 * ac[1] * scale, scale);
    }
  }
};

CanvasRenderingContext2D.prototype.drawAtom = function(a, x, y, scale) {
  var ac; // atom color
  var r = 0.12 * scale;
  this.lineWidth = 0.08 * scale;
  switch(a) {
    case "H" : ac = "#68C6E9"; r = 0.10 * scale; break;
    case "O" : ac = "#E70026"; break;
    case "C" : ac = "#000000"; break;
    case "Na" : ac = "#EDFB00"; break;
  }

  var startColor = color.average(ac, "#ffffff");
  startColor = color.average(startColor, "#ffffff");
  startColor = color.average(startColor, "#ffffff");

  var radgrad = this.createRadialGradient(x - 0.04 * scale, y - 0.04 * scale, 0.04 * scale, x, y, 0.16 * scale);
  radgrad.addColorStop(0, startColor);
  radgrad.addColorStop(1, ac);

  this.fillStyle = radgrad;
  this.fillCircle(x, y, r);
  this.fillStyle = "#000000";
  this.textAlign = "center";
  this.textBaseline = "middle";
  this.font = Math.floor(0.14 * scale) + "px sans-serif";
  this.fillText(a, x, y - 0.01 * scale, null);
};